<?php
class Ftp {
	private $_aConn = array ();
	private $_rConn;
	private $_aFileInfo = array ();
	
	public function __construct($sHost = null, $iPort = 21, $sUser = null, $sPassword = null, $bPasv = true) {
		if ($sHost !== null) {
			$this->connect ( $sHost, $iPort, $sUser, $sPassword, $bPasv );
		}
	}
	
	public function connect($sHost, $iPort = 21, $sUser, $sPassword, $bPasv = true) {
		try {
			if (! extension_loaded ( 'ftp' )) {
				throw new ExceptionExt ( 'php的ftp扩展没有安装' );
			}
			$iPort = intval ( $iPort ) ? $iPort : 21;
			$sId = $this->_getId ( $sHost, $iPort );
			if (isset ( $this->_aConn [$sId] )) {
				$this->_rConn = $this->_aConn [$sId];
				return $this->_aConn [$sId];
			}
			$rConn = @ftp_connect ( $sHost, $iPort );
			if (! $rConn) {
				throw new ExceptionExt ( 'ftp连接失败' );
			}
			if (! @ftp_login ( $rConn, $sUser, $sPassword )) {
				throw new ExceptionExt ( 'ftp验证用户名密码错误' );
			} else {
				ftp_pasv ( $rConn, $bPasv );
				$this->_rConn = $this->_aConn [$sId] = $rConn;
				return $rConn;
			}
		} catch ( ExceptionExt $e ) {
			$e->showError ();
		}
	
	}
	
	private function _getId($sHost, $iPort) {
		return md5 ( $sHost . ':' . $iPort );
	}
	
	public function setConn($rConn) {
		if (is_resource ( $rConn )) {
			$this->_rConn = $rConn;
		}
	}
	
	/**
	 * 列出ftp上指定目录下的文件了列表
	 * 
	 * @param string $sPath
	 * @param string $sType all所有 dir目录 file文件
	 * @param string $sMatch 可以使用通配符*和?
	 * @param array $aResult
	 * @return array 如果目录不存在的话返回空数组
	 */
	public function ls($sPath, $sType = 'all', $sMatch = null, $aResult = array()) {
		$rConn = $this->_rConn;
		$bSelf = false;
		$aFiles = @ftp_rawlist ( $rConn, $sPath );
		if ($aFiles == false) {
			return array();
		}
		if ($sMatch != null && $sMatch != '*') {
			$sMatch = str_replace ( '.', '\.', $sMatch );
			$sMatch = str_replace ( '*', '.*', $sMatch );
			$sMatch = str_replace ( '?', '.?', $sMatch );
			$sMatch = '/^' . $sMatch . '$/';
		}
		$iYear = date ( 'Y' );
		if (count ( $aFile ) == 1) {
			$aInfo = preg_split ( '/\s+/s', $aFile [0] );
			if ($aInfo [8] == basename ( $sPath )) {
				$bSelf = true;
			}
		}
		foreach ( $aFiles as $sRow ) {
			$aInfo = preg_split ( '/\s+/s', $sRow );
			$sName = $aInfo [8];
			if ($bSelf) {
				$sFile = $sPath;
			} elseif (substr ( $sPath, - 1 ) != '/') {
				$sFile = $sPath . '/' . $sName;
			} else {
				$sFile = $sPath . $sName;
			}
			$sFileId = md5 ( $sFile );
			$this->_aFileInfo [$sFileId] ['dir'] = $bDir = $aInfo [0] {0} == 'd';
			$this->_aFileInfo [$sFileId] ['name'] = $sName;
			$this->_aFileInfo [$sFileId] ['size'] = $aInfo [4];
			$this->_aFileInfo [$sFileId] ['time'] = strtotime ( $aInfo [6] . ' ' . $aInfo [5] . ' ' . $iYear . ' ' . $aInfo [7] );
			$this->_aFileInfo [$sFileId] ['chmod'] = $aInfo [0];
			if ($sMatch != null) {
				if (! preg_match ( $sMatch, $sName )) {
					continue;
				}
			}
			if ($sType == 'dir' && $bDir) {
				$aResult [] = $sFile;
			} elseif ($sType == 'file' && ! $bDir) {
				$aResult [] = $sFile;
			} elseif ($sType == 'all') {
				$aResult [] = $sFile;
			} elseif ($sType == 'tree') {
				$aResult [] = $sFile;
				if ($bDir) {
					$aResult = $this->_ls ( $sFile, 'tree', null, $aResult );
				}
			}
		}
		return $aResult;
	}
	
	public function __destruct() {
		foreach ( $this->_aConn as $rRow ) {
			if (is_resource ( $rRow )) {
				ftp_close ( $rRow );
			}
		}
	}
	
	public function close($rConn = null) {
		if ($rConn) {
			ftp_close ( $rConn );
		} else {
			foreach ( $this->_aConn as $rRow ) {
				ftp_close ( $rRow );
			}
		}
	}
	
	public function getSize($sFile) {
		$sId = md5 ( $sFile );
		if (isset ( $this->_aFileInfo [$sId] )) {
			return $this->_aFileInfo [$sId] ['size'];
		} else {
			$this->ls ( $sFile, 'file' );
			if (isset ( $this->_aFileInfo [$sId] )) {
				return $this->_aFileInfo [$sId] ['size'];
			}
		}
		return 0;
	}
	
	public function getTime($sFile) {
		$sId = md5 ( $sFile );
		if (isset ( $this->_aFileInfo [$sId] )) {
			return $this->_aFileInfo [$sId] ['time'];
		} else {
			$this->ls ( $sFile, 'file' );
			if (isset ( $this->_aFileInfo [$sId] )) {
				return $this->_aFileInfo [$sId] ['time'];
			}
		}
		return false;
	}
	
	public function isDir($sFile) {
		$sId = md5 ( $sFile );
		if (isset ( $this->_aFileInfo [$sId] )) {
			return $this->_aFileInfo [$sId] ['dir'];
		} else {
			$this->ls ( $sFile, 'file' );
			if (isset ( $this->_aFileInfo [$sId] )) {
				return $this->_aFileInfo [$sId] ['dir'];
			}
		}
		return false;
	}
	
	public function get($sRemotePath, $sLocalPath, $sModel = FTP_BINARY) {
		$rConn = $this->_rConn;
		return @ftp_get ( $rConn, $sLocalPath, $sRemotePath, $sModel );
	}
	
	public function rm($sPath) {
		$rConn = $this->_rConn;
		if ($this->isDir ( $sPath )) {
		
		} else {
			return @ftp_delete ( $rConn, $sPath );
		}
	}
}